﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Web;

namespace CommonEngine.SDK
{
    /// <summary>
    /// RESTful访问器
    /// </summary>
    /// <typeparam name="T">操作实体</typeparam>
    public class RestSDK<T> : RestSDK where T : class
    {
        public ResponseInfo ResultInfo { get; set; }
        public string Error { get; set; }

        /// <summary>
        /// 构造
        /// </summary>
        public RestSDK(string DomainUrl, string RouteUrl)
            : base(DomainUrl, RouteUrl)
        {
        }
        public RestSDK(string DomainUrl, string RouteUrl, string token)
            : base(DomainUrl, RouteUrl, token)
        {
        }

        public List<T> Select()
        {
            try
            {
                ResponseInfo info = null;
                var Ls = this.Get_All_Data<T>(out info);
                this.ResultInfo = info;
                return Ls;
            }
            catch (Exception ex)
            {
                this.Error = ex.Message;
                return null;
            }
        }

        public T Find(string IdKey)
        {
            try
            {
                ResponseInfo info = null;
                var en = this.Get_Data<T>(IdKey, out info);
                this.ResultInfo = info;
                return en;
            }
            catch (Exception ex)
            {
                this.Error = ex.Message;
                return null;
            }
        }
        public bool Add(T en)
        {
            try
            {
                ResponseInfo info = null;
                var isok = this.Post_Add_Data<T>(en, out info);
                this.ResultInfo = info;
                return isok;
            }
            catch (Exception ex)
            {
                this.Error = ex.Message;
                return false;
            }
        }
        public bool Update(string IdKey, T en)
        {
            try
            {
                ResponseInfo info = null;
                var isok = this.Put_Update_Data<T>(IdKey, en, out info);
                this.ResultInfo = info;
                return isok;
            }
            catch (Exception ex)
            {
                this.Error = ex.Message;
                return false;
            }
        }
        public bool Delete(string IdKey)
        {
            try
            {
                ResponseInfo info = null;
                var isok = this.Delete_Data(IdKey, out info);
                this.ResultInfo = info;
                return isok;
            }
            catch (Exception ex)
            {
                this.Error = ex.Message;
                return false;
            }
        }

        public string Get(Dictionary<string, object> parameters)
        {
            try
            {
                ResponseInfo info = null;
                var result = this.Get_String(parameters, out info);
                this.ResultInfo = info;
                return result;
            }
            catch (Exception ex)
            {
                this.Error = ex.Message;
                return null;
            }
        }
        public string Post(Dictionary<string, object> data, Dictionary<string, object> parameters = null)
        {
            try
            {
                ResponseInfo info = null;
                var result = this.Post_String(parameters, data, out info);
                this.ResultInfo = info;
                return result;
            }
            catch (Exception ex)
            {
                this.Error = ex.Message;
                return null;
            }
        }
        public string Put(Dictionary<string, object> data, Dictionary<string, object> parameters = null)
        {
            try
            {
                ResponseInfo info = null;
                var result = this.Put_String(parameters, data, out info);
                this.ResultInfo = info;
                return result;
            }
            catch (Exception ex)
            {
                this.Error = ex.Message;
                return null;
            }
        }
        public string Delete(Dictionary<string, object> parameters)
        {
            try
            {
                ResponseInfo info = null;
                var result = this.Delete_String(parameters, out info);
                this.ResultInfo = info;
                return result;
            }
            catch (Exception ex)
            {
                this.Error = ex.Message;
                return null;
            }
        }

        public string SendFile(Dictionary<string, object> parameters, string filePath)
        {
            try
            {
                ResponseInfo info = null;
                string url = this.Call_File(parameters);
                var result = this.HttpSendFile(url, filePath, out info);
                this.ResultInfo = info;
                return result;
            }
            catch (Exception ex)
            {
                this.Error = ex.Message;
                return null;
            }
        }
    }

    /// <summary>
    /// 要求服务按规则实现
    /// </summary>
    public class RestSDK
    {
        protected string DomainPath = "";
        protected string RoutePath = "";
        const string BOUNDARY = "MIME_boundary";
        const string CRLF = "\r\n";

        public string Token { get; set; }
        /// <summary>
        /// 构造
        /// </summary>
        public RestSDK(string DomainUrl, string RouteUrl)
        {
            this.DomainPath = DomainUrl;    //服务地址
            this.RoutePath = RouteUrl;      //路由地址
        }
        public RestSDK(string DomainUrl, string RouteUrl, string token)
        {
            this.DomainPath = DomainUrl;    //服务地址--http://localhost:58670
            this.RoutePath = RouteUrl;      //路由地址--api/GetToken
            this.Token = token;             //授权令牌
        }

        protected string Call_On(Dictionary<string, object> parameters)
        {
            string Url = this.RoutePath + "/";
            var requestUrl = this.AttachParameter(parameters);
            Url += this.RoutePath.LastIndexOf("?") > 0 ? requestUrl : (requestUrl == "" ? "" : "?") + requestUrl;
            return Url;
        }
        protected string Call_File(Dictionary<string, object> parameters)
        {
            string Url = this.DomainPath + @"/" + this.RoutePath + @"/";
            var requestUrl = this.AttachParameter(parameters);
            Url += this.RoutePath.LastIndexOf("?") > 0 ? requestUrl : (requestUrl == "" ? "" : "?") + requestUrl;
            return Url;
        }
        private HttpClient MakeClient(string DomainPath)
        {
            HttpClient client = new HttpClient();
            client.BaseAddress = new Uri(DomainPath + @"/"); //基本的API URL            
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); //默认希望响应使用Json序列化

            if (!string.IsNullOrEmpty(this.Token))
                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token); //授权

            //模拟标头
            client.DefaultRequestHeaders.Add("UserAgent", "Mozilla/5.0 (Windows NT 5.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1");
            client.Timeout = TimeSpan.FromSeconds(2 * 60);
            return client;
        }
        private ResponseInfo Collect_Info(HttpResponseMessage response, string result = null)
        {
            ResponseInfo RInfo = new ResponseInfo();
            RInfo.Associated = response.Headers.Location != null ? response.Headers.Location.ToString() : "";
            RInfo.StatusCode = response.StatusCode.ToString();
            RInfo.ReplyMsg = CommonOP.ConvertStringDecode(response.ReasonPhrase);
            RInfo.Result = result;
            return RInfo;
        }
        private string AttachParameter(Dictionary<string, object> parameters)
        {
            var postData = new StringBuilder();
            bool hasAndParameter = false;

            if (parameters != null)
            {
                foreach (var parameter in parameters)
                {
                    string name = parameter.Key;
                    if (string.IsNullOrEmpty(name))
                        continue;

                    string value = parameter.Value == null ? "" : parameter.Value.ToString();
                    if (hasAndParameter)
                        postData.Append("&");

                    postData.Append(name);
                    postData.Append("=");

                    if (!string.IsNullOrEmpty(value))
                        postData.Append(HttpUtility.UrlEncode(value));

                    hasAndParameter = true;
                }
            }
            return postData.ToString();
        }
        private string FilterString(string Data)
        {
            if (string.IsNullOrWhiteSpace(Data))
                return string.Empty;

            string str = CommonOP.GetInterceptString(Data, '\"');
            return str;
        }

        protected List<T> Get_All_Data<T>(out ResponseInfo RInfo) where T : class
        {
            RInfo = null;
            HttpClient client = this.MakeClient(this.DomainPath);
            var t = client.GetAsync(this.RoutePath);
            t.Wait();
            var response = t.Result;

            string json = response.Content.ReadAsStringAsync().Result;
            List<T> Data = JsonHelper.DeserializeJsonToList<T>(json);

            RInfo = this.Collect_Info(response);
            return Data;
        }

        protected T Get_Data<T>(string ID, out ResponseInfo RInfo) where T : class
        {
            RInfo = null;
            if (string.IsNullOrWhiteSpace(ID))
                return default(T);

            HttpClient client = this.MakeClient(this.DomainPath);
            var t = client.GetAsync(this.RoutePath + "/" + ID);
            t.Wait();
            var response = t.Result;

            string json = response.Content.ReadAsStringAsync().Result;
            T En = JsonHelper.DeserializeJsonToObject<T>(json);

            RInfo = this.Collect_Info(response);
            return En;
        }


        protected bool Post_Add_Data<T>(T en, out ResponseInfo RInfo) where T : class
        {
            RInfo = null;
            if (en == null)
                return false;

            HttpClient client = this.MakeClient(this.DomainPath);
            HttpContent content = new StringContent(JsonHelper.SerializeObjectNoRN(en), Encoding.GetEncoding("UTF-8"), "application/json");
            var t = client.PostAsync(this.RoutePath, content);
            t.Wait();
            var response = t.Result;

            string result = response.Content.ReadAsStringAsync().Result;
            RInfo = this.Collect_Info(response, result);
            return true;
        }

        protected bool Put_Update_Data<T>(string ID, T en, out ResponseInfo RInfo) where T : class
        {
            RInfo = null;
            if (en == null || string.IsNullOrWhiteSpace(ID))
                return false;

            HttpClient client = this.MakeClient(this.DomainPath);
            HttpContent content = new StringContent(JsonHelper.SerializeObjectNoRN(en), Encoding.GetEncoding("UTF-8"), "application/json");
            var t = client.PutAsync(this.RoutePath + "/" + ID, content);
            t.Wait();
            var response = t.Result;

            string result = response.Content.ReadAsStringAsync().Result;
            RInfo = this.Collect_Info(response, result);
            return true;
        }

        protected bool Delete_Data(string ID, out ResponseInfo RInfo)
        {
            RInfo = null;
            if (string.IsNullOrWhiteSpace(ID))
                return false;

            HttpClient client = this.MakeClient(this.DomainPath);
            var t = client.DeleteAsync(this.RoutePath + "/" + ID);
            t.Wait();
            var response = t.Result;

            string result = response.Content.ReadAsStringAsync().Result;
            RInfo = this.Collect_Info(response, result);
            return true;
        }


        public virtual string Get_String(Dictionary<string, object> parameters, out ResponseInfo RInfo)
        {
            HttpClient client = this.MakeClient(this.DomainPath);
            var t = client.GetAsync(this.Call_On(parameters));
            t.Wait();
            var response = t.Result;

            string result = response.Content.ReadAsStringAsync().Result;
            RInfo = this.Collect_Info(response, result);
            return this.FilterString(result);
        }

        public virtual string Post_String(Dictionary<string, object> parameters, Dictionary<string, object> data, out ResponseInfo RInfo)
        {
            HttpClient client = this.MakeClient(this.DomainPath);

            var requestData = this.AttachParameter(data);
            HttpContent content = new StringContent(requestData, Encoding.GetEncoding("UTF-8"), "application/x-www-form-urlencoded");

            var t = client.PostAsync(this.Call_On(parameters), content);
            t.Wait();
            var response = t.Result;

            string result = response.Content.ReadAsStringAsync().Result;
            RInfo = this.Collect_Info(response, result);
            return this.FilterString(result);
        }

        public virtual string Put_String(Dictionary<string, object> parameters, Dictionary<string, object> data, out ResponseInfo RInfo)
        {
            HttpClient client = this.MakeClient(this.DomainPath);

            var requestData = this.AttachParameter(data);
            HttpContent content = new StringContent(requestData, Encoding.GetEncoding("UTF-8"), "application/x-www-form-urlencoded");

            var t = client.PutAsync(this.Call_On(parameters), content);
            t.Wait();
            var response = t.Result;

            string result = response.Content.ReadAsStringAsync().Result;
            RInfo = this.Collect_Info(response, result);
            return this.FilterString(result);
        }

        public virtual string Delete_String(Dictionary<string, object> parameters, out ResponseInfo RInfo)
        {
            HttpClient client = this.MakeClient(this.DomainPath);
            var t = client.DeleteAsync(this.Call_On(parameters));
            t.Wait();
            var response = t.Result;

            string result = response.Content.ReadAsStringAsync().Result;
            RInfo = this.Collect_Info(response, result);
            return this.FilterString(result);
        }

        /// <summary>
        /// 发送文件(文件不能超过4M，大文件用IOCP)
        /// </summary>
        public string HttpSendFile(string url, string filePath, out ResponseInfo RInfo)
        {
            RInfo = new ResponseInfo();
            if (string.IsNullOrWhiteSpace(url))
                return null;

            if (!File.Exists(filePath))
                return null;

            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
            req.Method = "POST";
            req.ProtocolVersion = HttpVersion.Version11;

            if (!string.IsNullOrEmpty(Token))
                req.Headers.Add("Authorization", "Bearer " + Token);

            req.ContentType = "multipart/form-data; boundary=" + BOUNDARY;
            FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
            fs.Seek(0, SeekOrigin.Begin);
            BinaryReader br = new BinaryReader(fs);
            byte[] image = br.ReadBytes((int)fs.Length);
            br.Close();
            fs.Close();
            string name = Path.GetFileName(filePath);
            string strReq1 = "--" + BOUNDARY + CRLF
                + "Content-Disposition: form-data; name=" + name + "; filename=" + name + CRLF
                + "Content-Transfer-Encoding: binary" + CRLF
                + "Content-Type: application/octet-stream" + CRLF + CRLF;

            string strReq2 = CRLF + "--" + BOUNDARY + "--" + CRLF;
            byte[] postDataBytes1 = Encoding.ASCII.GetBytes(strReq1);
            byte[] postDataBytes2 = Encoding.ASCII.GetBytes(strReq2);
            int len = postDataBytes1.Length + postDataBytes2.Length + image.Length;
            req.ContentLength = len;

            try
            {
                Stream requestStream = req.GetRequestStream();
                requestStream.Write(postDataBytes1, 0, postDataBytes1.Length);
                requestStream.Write(image, 0, image.Length);
                requestStream.Write(postDataBytes2, 0, postDataBytes2.Length);
                requestStream.Close();

                HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
                StreamReader responseReader = new StreamReader(resp.GetResponseStream(), Encoding.UTF8);
                string str = responseReader.ReadToEnd();

                //与HttpClient不同，描述不能传递参数了
                RInfo.Associated = resp.Server;
                RInfo.StatusCode = resp.StatusCode.ToString();
                RInfo.ReplyMsg = resp.StatusDescription;
                RInfo.Result = str;

                return str;
            }
            catch (WebException ex)
            {
                RInfo.StatusCode = ex.Status.ToString();
                RInfo.Result = ex.Message;
                return WebExceptionHandle(ex);
            }
        }
        public string WebExceptionHandle(WebException ex)
        {
            if (ex == null)
                return string.Empty;

            var webResponse = ex.Response as HttpWebResponse;
            if (webResponse == null)
                return string.Empty;

            var characterSet = string.IsNullOrEmpty(webResponse.CharacterSet) ? "UTF-8" : webResponse.CharacterSet;
            var encoding = Encoding.GetEncoding(characterSet);
            using (var stream = webResponse.GetResponseStream())
            {
                using (var reader = new StreamReader(stream, encoding))
                {
                    var str = reader.ReadToEnd();
                    if (string.IsNullOrWhiteSpace(str))
                        str = ex.ToString();

                    if (webResponse != null)
                        webResponse.Close();

                    return str;
                }
            }
        }
    }
}
